package com.linkai.JUC._08_pool;

import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @Description: join() 方法的使用。3种线程池的对比
 *
 * @Author: 林凯
 * @Date: 2021/11/14 21:36
 */
public class ThreadPoolDemo03 {

    // 不使用线程池，同时测试 join() 方法
    public static void test01() throws InterruptedException {
        Long start = System.currentTimeMillis();
        Random random = new Random();
        ArrayList<Integer> arrayList = new ArrayList<>();
        for (int i = 0; i < 20000; i++) {
            int j = i;
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    arrayList.add(random.nextInt());
                    System.out.println(j);
                }
            });
            thread.start();
            /*
                使用 join() 方法可以让线程按照自己的顺序去执行
                调用此方法的线程被阻塞，即主线程阻塞，该线程执行完成之后，主线程才被唤醒。
            * */
            thread.join();
        }

        System.out.println("时间：" + (System.currentTimeMillis() - start));
        System.out.println("大小：" + arrayList.size());


//        while (Thread.activeCount() > 2) {
//            // 除了main线程，和gc线程之外，还有线程在执行的话，就一直循环
//            // 保证打印最后的长度的时候，其他线程已经执行完毕了。
//        }
    }

    // 使用线程池
    public static void test02() throws InterruptedException {
        Long start = System.currentTimeMillis();
        Random random = new Random();
        ArrayList<Integer> arrayList = new ArrayList<>();
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 20000; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    arrayList.add(random.nextInt());
                }
            });
        }

        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.DAYS);
        System.out.println("时间：" + (System.currentTimeMillis() - start));
        System.out.println("大小：" + arrayList.size());

    }

    // 3种线程池的对比
    public static void test03() {

        class MyTask implements Runnable {
            int i = 0;

            public MyTask(int i) {
                this.i = i;
            }

            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "做第" + i + "个任务");
                try {
                    TimeUnit.SECONDS.sleep(1);      // 模拟业务逻辑
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        /*
            使用3中不同的线程池来完成任务，此时在总耗时中，业务逻辑耗时占比很大，线程创建切换耗时较少。
            所以线程越多，处理起来越快。
        * */
        ExecutorService executorService1 = Executors.newCachedThreadPool();
        ExecutorService executorService2 = Executors.newFixedThreadPool(10);
        ExecutorService executorService3 = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 100; i++) {
            executorService3.execute(new MyTask(i));
        }
    }


    public static void main(String[] args) throws InterruptedException {
        /*
            说明使用线程池明显要比不使用线程池更快。
            运行的总时间应该综合考虑2方面的因素
            （1）线程创建，切换的耗时
            （2）业务任务耗时
        * */
//        test01();        // 4035ms
//        test02();       // 32ms

        test03();

    }
}
